﻿using System;
using SSTU.Contract.Common;
using System.Threading;

namespace SSTU.Facade
{
	public class FacadeBase
	{
		/// <summary>
		/// Does the some facade action and returns facade response.
		/// </summary>
		/// <param name="dataSource">The data source of the action.</param>
		/// <param name="action">The facade action.</param>
		protected FacadeResponse Do(FacadeResponseDataSource dataSource, Action action)
		{
			bool isSuccess = false;
			Exception error = null;
			try
			{
				action();
				isSuccess = true;
			}
			catch (Exception e)
			{
				isSuccess = false;
				error = e;
			}
			FacadeResponse response = null;
			if (isSuccess)
			{
				response = FacadeResponse.Completed(dataSource);
			}
			else
			{
				response = FacadeResponse.Failed(error, dataSource);
			}
			return response;
		}

		/// <summary>
		/// Does the some facade action and returns facade response.
		/// </summary>
		/// <param name="dataSource">The data source of the action.</param>
		/// <param name="action">The facade action, which returns data.</param>
		protected FacadeResponse<T> Do<T>(FacadeResponseDataSource dataSource, Func<T> action)
		{
			T data = default(T);
			bool isSuccess = false;
			Exception error = null;
			try
			{
				data = action();
				isSuccess = true;
			}
			catch (Exception e)
			{
				isSuccess = false;
				error = e;
			}
			FacadeResponse<T> response = null;
			if (isSuccess)
			{
				response = FacadeResponse<T>.Completed<T>(data, dataSource);
			}
			else
			{
				response = FacadeResponse<T>.Failed<T>(error, dataSource);
			}
			return response;
		}

		/// <summary>
		/// Does the some facade action asynchronously and invokes callback.
		/// </summary>
		/// <param name="dataSource">The data source.</param>
		/// <param name="action">The facade action.</param>
		/// <param name="asyncCallback">The async callback.</param>
		protected void DoAsync(FacadeResponseDataSource dataSource, Action action, FacadeAsyncCallback asyncCallback)
		{
			var syncContext = SynchronizationContext.Current;
			ThreadPool.QueueUserWorkItem(state =>
			{
				var facadeResponse = Do(dataSource, action);
				if (syncContext == null)
				{
					asyncCallback(facadeResponse);
				}
				else
				{
					syncContext.Post(facadeResponseObject => { asyncCallback((FacadeResponse)facadeResponseObject); }, facadeResponse);
				}
			});
		}

		/// <summary>
		/// Does the some facade action asynchronously and invokes callback.
		/// </summary>
		/// <param name="dataSource">The data source.</param>
		/// <param name="action">The facade action.</param>
		/// <param name="asyncCallback">The async callback.</param>
		protected void DoAsync<T>(FacadeResponseDataSource dataSource, Func<T> action, FacadeAsyncCallback<T> asyncCallback)
		{
			var syncContext = SynchronizationContext.Current;
			ThreadPool.QueueUserWorkItem(state =>
			{
				var facadeResponse = Do<T>(dataSource, action);
				if (syncContext == null)
				{
					asyncCallback(facadeResponse);
				}
				else
				{
					syncContext.Post(facadeResponseObject => { asyncCallback((FacadeResponse<T>)facadeResponseObject); }, facadeResponse);
				}
			});
		}
	}
}
